#if !defined(glib_g3_trimesh_h)
#define glib_g3_trimesh_h

#include "vector.h"
#include "../callback.h"
#include "hash_grid.h"

#include <vector>
#include <iostream>

namespace g3 {

struct edge
{
	int v[2];
	edge() 
		{ }
	edge(int v0, int v1)
		{ v[0] = v0; v[1] = v1; }
	bool operator < (const edge & rhs) const { 
		if (v[0] != rhs.v[0])
			return v[0] < rhs.v[0];
		if (v[1] != rhs.v[1])
			return v[1] < rhs.v[1];
		return false;
	}
	bool operator == (const edge & rhs) const
	{
		return v[0] == rhs.v[0] && v[1] == rhs.v[1];
	}
	bool operator != (const edge & rhs) const
	{
		return !operator == (rhs);
	}
};

inline std::ostream & operator << (std::ostream & out, const edge & e)
{
	return out << "{" << e.v[0] << ", " << e.v[1] << "}";
}

struct face
{
	int	v[3];
	face()
		{ }
	face(int v0, int v1, int v2)
		{ v[0] = v0; v[1] = v1; v[2] = v2; }
	bool operator < (const face & rhs) const { 
		if (v[0] != rhs.v[0])
			return v[0] < rhs.v[0];
		if (v[1] != rhs.v[1])
			return v[1] < rhs.v[1];
		if (v[2] != rhs.v[2])
			return v[2] < rhs.v[2];
		return false;
	}
	int index_of(int vi) const
	{
		if (vi == v[0])
			return 0;
		if (vi == v[1])
			return 1;
		if (vi == v[2])
			return 2;
		return -1;
	}
};

inline std::ostream & operator << (std::ostream & out, const face & f)
{
	return out << "{" << f.v[0] << ", " << f.v[1] << ", " << f.v[2] << "}";
}

struct trimesh
{
	std::vector<g3::vector>		coords;
	std::vector<face>			faces;

	void clear()
	{
		coords.clear();
		faces.clear();
	}
};

inline g3::edge orient_edge(int v0, int v1, int & orient)
{
	if (v0 <= v1)
	{
		orient = 1;
		return g3::edge(v0, v1);
	}
	else
	{
		orient = -1;
		return g3::edge(v1, v0);
	}
}

inline g3::face orient_face(int v0, int v1, int v2, int & orient) 
{

	if (v0 <= v1) 
	{
		if (v1 <= v2) 
		{
			// v0 <= v1 <= v2
			orient = 1;
			return g3::face(v0, v1, v2);
		}
		else 
		{
			if (v0 <= v2) 
			{
				// v0 <= v2 <= v1
				orient = -1;
				return g3::face(v0, v2, v1);
			} 
			else 
			{
				// v2 <= v0 <= v1
				orient = 1;
				return g3::face(v2, v0, v1);
			}
		}
	} 
	else 
	{
		if (v0 <= v2) 
		{
			// v1 <= v0 <= v2
			orient = -1;
			return g3::face(v1, v0, v2);
		} 
		else 
		{
			if (v1 <= v2) 
			{
				// v1 <= v2 <= v0
				orient = 1;
				return g3::face(v1, v2, v0);
			} 
			else 
			{
				// v2 <= v1 <= v0
				orient = -1;
				return g3::face(v2, v1, v0);
			}
		}
	}
}

struct edge_info
{
	int f[2]; // two face indices
	int e[2]; // index of this edge in each face
	edge_info()
	{
		f[0] = f[1] = -1;
		e[0] = e[1] = -1;
	}
};

struct face_info
{
	int e[3]; // three edge indices
	int f[3]; // index of this face in each edge
	face_info()
	{
		e[0] = e[1] = e[2] = -1;
		f[0] = f[1] = f[2] = -1;
	}
};

struct trimesh_topology
{
	std::vector<edge_info>	edges;
	std::vector<face_info>	faces;
};

bool compute_topology(const trimesh &, trimesh_topology &, glib::progress_callback * = 0);

void compute_normals(const trimesh &, std::vector<vector> &);

void build_vertex_grid(const std::vector<vector> &, hash_grid<int> &, double cell_size);

void build_face_grid(const std::vector<vector> &, const std::vector<face> &, hash_grid<int> &, double cell_size);

bool project_point_on_surface(const vector & v, const trimesh & s, 
							  const hash_grid<int> & face_grid, double max_distance, 
							  int & face_index, vector & bary_coords);

void simple_smooth(trimesh &, double alpha);

void simple_smooth(trimesh &, double alpha, const std::vector<bool> & mask);

} // g3

#endif // glib_g3_trimesh_h
